/**
 * 
 */
package jmat.impl;

import java.util.ArrayList;
import java.util.List;

import jmat.intr.Matrix;

/**
 * @author Aram Altschudjian
 *
 */

public class Address{
	private int dimensions;
	private Integer[] sizes;
	private Object[] data;
	private Matrix matrix;
	private int matrixDimensions;
	private int entries;
	
	public Address(Matrix matrix, Object address){
		this.matrix = matrix;
		dimensions = 0;
		matrixDimensions = this.matrix.getDim();
		sizes = new Integer[matrixDimensions];
		data = new Object[matrixDimensions];
		processAddr(address);
		entries = 1;
		for(int i = 0; i < dimensions; ++i){
			entries *= sizes[i];
		}
	}
	
	@SuppressWarnings("unchecked")
	private void processAddr(Object address){
		if(matrix.isAll(address)){
			while(dimensions < matrixDimensions){
				fillAll();
			}
		} else if(address instanceof Integer){
			data = matrix.convert((Integer)address);
			for(; dimensions < matrixDimensions; ++dimensions){
				sizes[dimensions] = 1;
			}
		} else if(address instanceof String){
			process(address);
			while(dimensions < matrixDimensions){
				fillAll();
			}
		} else if(address instanceof Object[]){
			Object[] array = (Object[])address;
			if(array.length != matrixDimensions){
				throw new IllegalArgumentException("Address format mismatch.");
			}
			while(dimensions < matrixDimensions){
				process(array[dimensions]);
			}
		} else if(address instanceof ArrayList){
			ArrayList<Object> list = (ArrayList<Object>)address;
			if(list.size() != matrixDimensions){
				throw new IllegalArgumentException("Address format mismatch.");
			}
			while(dimensions < matrixDimensions){
				process(list.get(dimensions));
			}
		} else{
			throw new IllegalArgumentException("Address format mismatch.");
		}
	}

	@SuppressWarnings("unchecked")
	private void process(Object coordinate) {
		if(matrix.isAll(coordinate)){
			fillAll();
			return;
		} else if(coordinate instanceof Integer){
			sizes[dimensions] = 1;
			if((Integer)coordinate < 0){
				data[dimensions] = matrix.size(dimensions) + (Integer)coordinate;
			} else{
				data[dimensions] = coordinate;
			}
		} else if(coordinate instanceof String){
			sizes[dimensions] = 1;
			data[dimensions] = matrix.resolve(0, (String)coordinate);
		} else if(coordinate instanceof List){
			List<Object> list = (List<Object>)coordinate;
			sizes[dimensions] = list.size();
			Integer[] coords = new Integer[sizes[dimensions]];
			for(int i = 0; i < sizes[dimensions]; ++i){
				Object coord = list.get(i);
				if(coord instanceof Integer){
					if((Integer)coord < 0){
						coords[i] = matrix.size(dimensions) + (Integer)coord;
					} else{
						coords[i] = (Integer)coord;
					}
				} else if(coord instanceof String){
					coords[i] = matrix.resolve(dimensions, (String)coord);
				} else{
					throw new IllegalArgumentException("Address format mismatch.");
				}
			}
			data[dimensions] = coords;
		} else if(coordinate instanceof Integer[]){
			Integer[] array = (Integer[])coordinate;
			sizes[dimensions] = array.length;
			data[dimensions] = array;
		} else if(coordinate instanceof Object[]){
			Integer[] array = (Integer[])coordinate;
			sizes[dimensions] = array.length;
			Integer[] coords = new Integer[sizes[dimensions]];
			for(int i = 0; i < sizes[dimensions]; ++i){
				Object coord = array[i];
				if(coord instanceof Integer){
					coords[i] = (Integer)coord;
				} else if(coord instanceof String){
					coords[i] = matrix.resolve(dimensions, (String)coord);
				} else{
					throw new IllegalArgumentException("Address format mismatch.");
				}
			}
			data[dimensions] = coords;
		} else{
			throw new IllegalArgumentException("Address format mismatch.");
		}
		++dimensions;
	}

	private void fillAll() {
		sizes[dimensions] = matrix.size(dimensions);
		Integer[] all = new Integer[sizes[dimensions]];
		for(int i = 0; i < sizes[dimensions]; ++i){
			all[i] = i;
		}
		data[dimensions] = all;
		++dimensions;
	}
	
	public boolean valid(){
		return dimensions == matrix.getDim();
	}
	
	public int entries(){
		return entries;
	}
	
	public Object get(int dimension){
		return data[dimension];
	}
	
	public int get(int dimension, int index){
		Object coordinates = data[dimension];
		if(coordinates instanceof Integer){
			if(index == 0){
				return (Integer)coordinates;	
			} else{
				throw new IndexOutOfBoundsException();
			}
		} else if(coordinates instanceof Integer[]){
			return ((Integer[])coordinates)[index];
		} else{
			throw new IllegalStateException("This really shouldn't happen.");
		}
	}
	
	public Integer[] size(){
		return sizes;
	}
	
	public int size(int dimension){
		return sizes[dimension];
	}
	
	public int[] max(){
		int[] maxima = new int[dimensions];
		for(int i = 0; i < dimensions; ++i){
			if(size(i) == 1){
				maxima[i] = (Integer)data[i];
			} else if(size(i) > 1){
				maxima[i] = 0;
				Integer[] coordinates = (Integer[])data[i];
				for(int j = 0; j < size(i); ++j){
					if(coordinates[j] > maxima[i]){
						maxima[i] = coordinates[j];
					}
				}
			} else{
				throw new IllegalArgumentException("Address format mismatch.");
			}
		}
		return maxima;
	}
}
